HmJS.$Import('core.core');

/*
---
version: 1.0.1-0
description: n Utility Class (mixin) allowing the instances of a class implementing it to use a simple crossbrowser caching system.
license: MIT-style
download: http://mootools.net/forge/p/cacheable
source: http://github.com/DEEO/Cacheable

authors:
- Kevin Chapelier (http://www.deeo.fr/)

requires:
core/1.3:
- Function
- Class
- Cookie
- JSON
provides: [Cacheable]
...
*/

HmJS.register('util.Cacheable', function ($ns) {

	var Cacheable = new Class({

		initialize: function () {
			this.setCacheStorage();
		},

		setCacheStorage: function (prefix, duration) {
			this.$cache = new Cacheable.Storages.Prefered(prefix || 'cache');
			if (duration) { this.$cache.duration = duration; }
		},

		setCache: function (index, value, duration) {
			return this.$cache.set(index, value, duration);
		},

		getCache: function (index) {
			return this.$cache.get(index);
		},

		unsetCache: function (index) {
			return this.$cache.unset(index);
		}

	});

	Cacheable.setPreferedStorage = function () {
		for (var x = 0, l = arguments.length; x < l; x++) {
			if (arguments[x] && typeOf(arguments[x]) == 'class' && arguments[x].supported) {
				Cacheable.Storages.Prefered = arguments[x];

				return true;
			}
		}

		Cacheable.Storages.Prefered = Cacheable.Storages.Hash;

		return false;
	};

	Cacheable.Storages = {

		Base: new Class({
			duration: 3600, //one hour
			prefix: null,
			host: null,

			initialize: function (prefix, host) {
				this.prefix = prefix ? prefix + '_' : '';
				this.host = host || window.location.hostname;
			},

			encodeData: function (value, duration) {
				var data = {
					'value': value,
					'expiredAt': Date.now() + (duration || this.duration) * 1000
				};

				return JSON.encode(data);
			},

			decodeData: function (data, index) {
				if (data) {
					data = JSON.decode(data);

					if (Date.now() > data.expiredAt) {
						this.unset(index);
					} else {
						return data.value;
					}
				}
			},

			extendValidity: function (index, duration) {
				var value = this.get(index);
				this.set(value, duration);
			}

		})

	};

	Cacheable.Storages.Local = new Class({

		Extends: Cacheable.Storages.Base,

		set: function (index, value, duration) {
			var data = this.encodeData(value, duration);
			localStorage.setItem(this.prefix + index, data);
			return true;
		},

		get: function (index) {
			var data = localStorage.getItem(this.prefix + index);
			return this.decodeData(data, index);
		},

		unset: function (index) {
			localStorage.removeItem(this.prefix + index);
			return true;
		}

	});

	Cacheable.Storages.Local.supported = (function () {
		try {
			return ('localStorage' in window) && window.localStorage !== null;
		} catch (e) {
			return false;
		}
	})();

	Cacheable.Storages.Session = new Class({

		Extends: Cacheable.Storages.Base,

		set: function (index, value, duration) {
			var data = this.encodeData(value, duration);
			sessionStorage.setItem(this.prefix + index, data);
			return true;
		},

		get: function (index) {
			var data = sessionStorage.getItem(this.prefix + index);
			return this.decodeData(data, index);
		},

		unset: function (index) {
			sessionStorage.removeItem(this.prefix + index);
			return true;
		}

	});

	Cacheable.Storages.Session.supported = (function () {
		try {
			return ('sessionStorage' in window) && window.sessionStorage !== null;
		} catch (e) {
			return false;
		}
	})();

	Cacheable.Storages.Global = new Class({

		Extends: Cacheable.Storages.Base,

		set: function (index, value, duration) {
			var data = this.encodeData(value, duration);
			globalStorage[this.host][this.prefix + index] = data;
			return true;
		},

		get: function (index) {
			var obj = globalStorage[this.host][this.prefix + index];
			if (typeOf(obj) == 'object') {
				return this.decodeData(obj.value, index);
			}
		},

		unset: function (index) {
			delete globalStorage[this.host][this.prefix + index];
			return true;
		}

	});

	Cacheable.Storages.Global.supported = (function () {
		try {
			return ('globalStorage' in window) && window.globalStorage !== null;
		} catch (e) {
			return false;
		}
	})();

	Cacheable.Storages.UserData = new Class({

		Extends: Cacheable.Storages.Base,

		set: function (index, value, duration) {
			if (Cacheable.Storages.UserData.element == null) {
				Cacheable.Storages.UserData.setElement();
			}

			var data = this.encodeData(value, duration);
			Cacheable.Storages.UserData.element.setAttribute(this.prefix + index, data);
			Cacheable.Storages.UserData.element.save('cache');
			return true;
		},

		get: function (index) {
			if (Cacheable.Storages.UserData.element == null) {
				Cacheable.Storages.UserData.setElement();
			}

			var data = Cacheable.Storages.UserData.element.getAttribute(this.prefix + index);
			return this.decodeData(data, index);
		},

		unset: function (index) {
			if (Cacheable.Storages.UserData.element == null) {
				Cacheable.Storages.UserData.setElement();
			}

			Cacheable.Storages.UserData.element.removeAttribute(this.prefix + index);
			Cacheable.Storages.UserData.element.save('cache');
			return true;
		}

	});

	Cacheable.Storages.UserData.element = null;

	Cacheable.Storages.UserData.setElement = function () {
		var el = document.createElement('div');
		el.style.display = 'none';
		document.body.appendChild(el);
		el.addBehavior('#default#userdata');
		el.load('cache');

		Cacheable.Storages.UserData.element = el;
	};

	Cacheable.Storages.UserData.supported = (function () {
		if (Browser.name == 'ie' && Browser.version < 9 && Browser.version >= 5) {
			try {
				var elTest = document.createElement('div');

				if ('addBehavior' in elTest && elTest.addBehavior !== null) {
					return true;
				}
			}
			catch (e) { }
		}

		return false;
	})();

	Cacheable.Storages.Cookie = new Class({

		Extends: Cacheable.Storages.Base,

		set: function (index, value, duration) {
			var data = this.encodeData(value, duration);

			Cookie.write(this.prefix + index, data, {
				'duration': Math.ceil((duration || this.duration) / 86400), //cookie duration is in days
				'domain': this.host
			});

			return true;
		},

		get: function (index) {
			var data = Cookie.read(this.prefix + index);
			return this.decodeData(data, index);
		},

		unset: function (index) {
			Cookie.dispose(this.prefix + index, { 'domain': this.host });
			return true;
		}

	});

	Cacheable.Storages.Cookie.supported = true;

	Cacheable.Storages.Hash = new Class({

		Extends: Cacheable.Storages.Base,

		set: function (index, value, duration) {
			var data = this.encodeData(value);
			Cacheable.Storages.Hash.container[this.prefix + index] = value;
			return true;
		},

		get: function (index) {
			return Cacheable.Storages.Hash.container[this.prefix + index];
		},

		unset: function (index) {
			delete Cacheable.Storages.Hash.container[this.prefix + index];
			return true;
		}

	});

	Cacheable.Storages.Hash.container = {};
	Cacheable.Storages.Hash.supported = true;

	Cacheable.setPreferedStorage(
			Cacheable.Storages.Local,
			Cacheable.Storages.Global,
			Cacheable.Storages.Session,
			Cacheable.Storages.UserData,
			Cacheable.Storages.Cookie);

	return Cacheable;
});